package com.yuw.consumerandproducer;

/**
 * 自定义栈
 */
public class MyStack {
    // 属性
    // 栈的存储位置
    private Object[] stack;
    // 栈的长度
    private int len;
    // 当前栈的位置下标
    private int index;

    // 只有一个构造方法，为了就是限制new入口，只有这一个可以用
    public MyStack(int len) {
        this.len = len;
        // 使用改长度初始化栈数组
        stack = new Object[len];
        // 初始化栈下标位置为-1
        index = -1;
    }

    // 行为

    /**
     * 出栈
     */
    // 修饰方法的用法
    public synchronized Object pop() {
        // 没有产品可以消费时，进入等待状态
        if (index < 0) {
            // 仓库已经空了，不能消费，请在仓库门口等待，等着生产者生产产品之后，发出通知以后再进行入消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 取出当前位置下标对应的元素值，出栈
        Object obj = stack[index];
        // 出栈以后，删除当前元素
        stack[index] = null;
        // 下标-1
        index--;

        // 取出元素之后，使用notifyAll通知在仓库等待的生产者线程继续生产入库
        this.notifyAll();

        return obj;
    }

    /**
     * 进栈
     *
     * @param obj 进栈元素
     */
    public void push(Object obj) {
        // 修饰语句块的用法
        synchronized (stack) { // synchronized枷锁对象只能是对象类型，不能使基本类型；如果是基本类型，则需要使用包装类类型；
            if (index >= len) {
                // 仓库已经满了，需要在仓库门口进入等待状态，等待消费者消费了产品之后，发出通知之后在进行入库操作
                try {
                    stack.wait(); // 调用wait的对象应该是被 synchronized 加锁的对象
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 当前位置的下标上移，同时进栈元素出入该位置
            index++;
            stack[index] = obj;
            // 已经生产了产品，通知正在等待的消费者进行消费
            stack.notifyAll();
        }
    }
}
